#include "stdafx.h"
#include "network.h"
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

#define MAX_BUFFER_SIZE  1024
using namespace boost::asio;
using namespace boost::asio::ip;

typedef struct BoostNetWorkObject
{
	io_service io_server;
	std::list<std::shared_ptr<tcp::socket>> socket_tclient;
	std::shared_ptr<udp::socket> socket_uclient;
	std::shared_ptr<tcp::acceptor> server_acceptor;
};

Network::Network(CRRMCommon* recv, unsigned short port) :
m_is_exit(true), mRecv(recv)
{
	m_boost_object = std::make_shared<BoostNetWorkObject>();
	m_boost_object->server_acceptor = std::make_shared<tcp::acceptor>(m_boost_object->io_server, tcp::endpoint(tcp::v4(), port));
	m_boost_object->socket_tclient.push_back(std::make_shared<tcp::socket>(m_boost_object->io_server));
	m_boost_object->socket_uclient = std::make_shared<udp::socket>(m_boost_object->io_server);
}
Network::Network(CRRMCommon* recv, unsigned short port, std::string ip) :
m_is_exit(true), mRecv(recv)
{
	m_boost_object = std::make_shared<BoostNetWorkObject>();
	m_boost_object->socket_tclient.push_back(std::make_shared<tcp::socket>(m_boost_object->io_server));
	m_boost_object->socket_uclient = std::make_shared<udp::socket>(m_boost_object->io_server);
}
Network::~Network(){
	m_is_exit = false;
	for (std::list<std::shared_ptr<tcp::socket>>::iterator it = 
		m_boost_object->socket_tclient.begin(); 
		it != m_boost_object->socket_tclient.end();it++)
		if ((*it).get()) (*it)->close();

	if (m_boost_object->socket_uclient.get())
		m_boost_object->socket_uclient->close();
	if (m_boost_object->server_acceptor.get())
		m_boost_object->server_acceptor->close();
}
unsigned short Network::GetLocalSocketPort()
{
	return m_boost_object->server_acceptor->local_endpoint().port();
}
std::list<unsigned short> Network::GetRemoteSocketPort()
{
	std::list<unsigned short> remote_ports;
	try{
		for (std::list<std::shared_ptr<tcp::socket>>::iterator it =
			m_boost_object->socket_tclient.begin();
			it != m_boost_object->socket_tclient.end(); it++)
			if ((*it)->is_open())
				remote_ports.push_back((*it)->remote_endpoint().port());
	}catch(std::exception&){}
	return remote_ports;
}
std::list<std::string> Network::GetSocketIP()
{
	std::list<std::string> remote_address;
	try{
		for (std::list<std::shared_ptr<tcp::socket>>::iterator it =
			m_boost_object->socket_tclient.begin();
			it != m_boost_object->socket_tclient.end(); it++)
			if ((*it)->is_open())
				remote_address.push_back((*it)->remote_endpoint().address().to_string());
	}catch(std::exception&){}
	return remote_address;
}
bool Network::ValidateUDP(PACKAGE_INFO& info)
{
	return info.checkcode == info.GetCheckSum(&info, FIELD_OFFSET(PACKAGE_INFO, checkcode))
		&& strcmp(info.magic, netpack_magic) == 0;
}


std::shared_ptr<Network> Network::CreateTCPServer(unsigned short port, CRRMCommon* recv)
{
	std::shared_ptr<Network> network = std::make_shared<Network>(recv, port);
	boost::thread([port, recv, network]{
		while (network.get() && network->m_is_exit)
		{
			try{
				//删除断开的连接
				for (std::list<std::shared_ptr<tcp::socket>>::iterator it = network->m_boost_object->socket_tclient.begin(); it != network->m_boost_object->socket_tclient.end();)
				{
					if (!(*it)->is_open())
						it = network->m_boost_object->socket_tclient.erase(it);
					else
						it++;
				}
				network->m_boost_object->socket_tclient.push_back(std::make_shared<tcp::socket>(network->m_boost_object->io_server));
				std::shared_ptr<tcp::socket> tcp_socket = network->m_boost_object->socket_tclient.back();
				network->m_boost_object->server_acceptor->accept(*tcp_socket);

				//接收数据
				std::thread([network, tcp_socket]{
					boost::system::error_code error;
					boost::array<char, MAX_BUFFER_SIZE> buf;
					while (network->m_is_exit && tcp_socket->is_open())
					{
						try{
							size_t read_len = tcp_socket->read_some(buffer(buf), error);
							if (error)	{
								//删除
								if (tcp_socket->is_open() && error != error::not_connected)
								{
									tcp_socket->close(boost::system::error_code());
									network->mRecv->NotifyStatus(NET_OFF, network.get(), &std::string(error.message()));
								}

								boost::this_thread::sleep(boost::posix_time::millisec(3));
								continue;
							}
							network->ResetByHeader(buf.data(), read_len);
						}
						catch (std::exception& e)
						{
							network->mRecv->NotifyStatus(NET_INFO, network.get(), &std::string(e.what()));
						}
					}
				}).detach();
				network->PackageProcess();
				recv->NotifyStatus(NET_ON, network.get(), &std::string("连接成功"));
			}
			catch (std::exception& e)
			{
				recv->NotifyStatus(NET_OFF, network.get(), &std::string(e.what()));
			}
			boost::this_thread::sleep(boost::posix_time::milliseconds(10));
		}
	}).detach();
	return network;
}

std::shared_ptr<Network> Network::CreateTCPClient(std::string ip, unsigned short port, CRRMCommon* recv, unsigned short local_port)
{
	std::shared_ptr<Network> network = std::make_shared<Network>(recv, port, ip);
	boost::thread([ip, port, recv, network, local_port]{
		bool is_first_connect = true;
		boost::system::error_code ec, ec_last;
		tcp::endpoint client_end_point(address::from_string(ip), port);
		//接收数据
		std::thread([network, local_port]{
			boost::system::error_code error;
			boost::array<char, MAX_BUFFER_SIZE> buf;
			while (network->m_is_exit)
			{
				try{
					size_t read_len = network->m_boost_object->socket_tclient.back()->read_some(buffer(buf), error);
					if (error)	{
						//删除
						if (network->m_boost_object->socket_tclient.back()->is_open() && error != error::not_connected)
						{
							network->mRecv->NotifyStatus(NET_OFF, network.get(), &std::string(error.message()));
							network->m_boost_object->socket_tclient.back()->close(error);
						}

						boost::this_thread::sleep(boost::posix_time::millisec(3));
						continue;
					}
					network->ResetByHeader(buf.data(), read_len);
				}
				catch (std::exception& e)
				{
					network->mRecv->NotifyStatus(NET_INFO, network.get(), &std::string(e.what()));
				}
			}
		}).detach();
		network->PackageProcess();
		//断线重连
		while (network.get() && network->m_is_exit) {
			try{
				if (!network->m_boost_object->socket_tclient.back()->is_open() && local_port!=0){
					network->m_boost_object->socket_tclient.back()->open(tcp::v4(), ec);
					network->m_boost_object->socket_tclient.back()->bind(tcp::endpoint(tcp::v4(), local_port), ec);
				}
				network->m_boost_object->socket_tclient.back()->connect(client_end_point, ec);
				if (ec){
					if (is_first_connect || ec_last != ec){
						is_first_connect = false;
						if (ec != boost::asio::error::already_connected&&ec != boost::asio::error::in_progress){
							recv->NotifyStatus(NET_OFF, network.get(), &std::string(ec.message()));
						}
					}
				}
				else{
					recv->NotifyStatus(NET_ON, network.get(), &std::string("连接成功"));
				}
				ec_last = ec;
			}
			catch (std::exception& e)
			{
				recv->NotifyStatus(NET_OFF,network.get(), &std::string(e.what()));
			}
			boost::this_thread::sleep(boost::posix_time::seconds(2));
		}
	}).detach();
	return network;
}

std::shared_ptr<Network> Network::CreateUDPClient(std::string ip, unsigned short port, CRRMCommon* recv, unsigned short local_port)
{
	boost::system::error_code err_code;
	std::shared_ptr<Network> network = std::make_shared<Network>(recv, port, ip);
	udp::endpoint broadcast_end_point(address::from_string(ip), port);
	if (local_port == 0){
		network->m_boost_object->socket_uclient->open(broadcast_end_point.protocol());
	}else{
		network->m_boost_object->socket_uclient->open(broadcast_end_point.protocol());
		network->m_boost_object->socket_uclient->bind(udp::endpoint(address::from_string("0.0.0.0"), local_port));
	}
	//network->m_boost_object->socket_uclient->set_option(boost::asio::socket_base::broadcast(true));
	network->m_boost_object->socket_uclient->connect(broadcast_end_point, err_code);

	//接收线程
	boost::thread([network]{
		network->UDPRecieve();
	}).detach();
	return network;
}

void Network::SendRawData(void* data, size_t len, bool bTcpTransfer)
{
	try{
		std::vector<char> data_send(len,0);
		memcpy(&(*data_send.begin()), data, len);
		boost::system::error_code ignored_error;
		if (bTcpTransfer)
			for (std::list<std::shared_ptr<tcp::socket>>::iterator it = m_boost_object->socket_tclient.begin(); it != m_boost_object->socket_tclient.end(); it++)
				(*it)->write_some(buffer(&(*data_send.begin()), data_send.size()), ignored_error);
		else
			m_boost_object->socket_uclient->send(buffer((char*)&data, sizeof(data)));
	}
	catch (std::exception& e){
		mRecv->NotifyStatus(NET_INFO, this, &std::string(e.what()));
	}
}
void Network::SendData(void* data, size_t len, DATATYPE dtype, bool bTcpTransfer)
{
	try{
		PACKAGE_INFO info((unsigned long)len, dtype);
		std::vector<char> data_send(len + FIELD_OFFSET(PACKAGE_INFO, data), 0);
		memcpy(&(*data_send.begin()), &info, FIELD_OFFSET(PACKAGE_INFO, data));
		memcpy(&(*data_send.begin()) + FIELD_OFFSET(PACKAGE_INFO, data), data, len);
		
		boost::system::error_code ignored_error;
		if (bTcpTransfer)
			for (std::list<std::shared_ptr<tcp::socket>>::iterator it = m_boost_object->socket_tclient.begin(); it != m_boost_object->socket_tclient.end(); it++)
				(*it)->write_some(buffer(&(*data_send.begin()), data_send.size()), ignored_error);
		else
			m_boost_object->socket_uclient->send(buffer((char*)&data, sizeof(data)));
	}
	catch (std::exception& e){
		mRecv->NotifyStatus(NET_INFO, this, &std::string(e.what()));
	}
}
void Network::BroadCastData(PACKAGE_INFO& data)
{
	try{
		m_boost_object->socket_uclient->send(buffer((char*)&data, sizeof(data)));
	}
	catch (std::exception& e)
	{
		mRecv->NotifyStatus(NET_INFO, this, &std::string(e.what()));
	}
}
void Network::UDPRecieve()
{
	boost::system::error_code error;
	boost::array<char, MAX_BUFFER_SIZE> buf;
	while (m_is_exit)
	{
		try{
			size_t read_len = m_boost_object->socket_uclient->receive(buffer(buf));
			PACKAGE_INFO package_info = *(PACKAGE_INFO*)(buf.data());
			if (read_len >= FIELD_OFFSET(PACKAGE_INFO, data) + package_info.datalen)
			{
				if (ValidateUDP(package_info))
					mRecv->NotifyStatus(NET_DATA, this, &package_info);
				else
					mRecv->NotifyStatus(NET_INFO, this, &std::string("接收到包检验码验证失败"));
			}
			else
			{
				mRecv->NotifyStatus(NET_INFO, this, &std::string("接收到包错误"));
			}
		}
		catch (std::exception& e)
		{
			mRecv->NotifyStatus(NET_INFO, this, &std::string(e.what()));
		}
	}
}
void Network::PackageProcess()
{
	//接收处理线程
	boost::thread([this]{
		while (m_is_exit)
		{
			try{
				m_mutex.lock();
				size_t lstSize = m_pack_buffer.size();
				if (lstSize == 0){
					m_mutex.unlock();
					boost::this_thread::sleep(boost::posix_time::milliseconds(2));
					continue;
				}
				
				std::vector<char> data = *&m_pack_buffer.front();
				m_mutex.unlock();

				if (ValidateUDP(*(PACKAGE_INFO*)(&*data.begin())))
					mRecv->NotifyStatus(NET_DATA, this, &*data.begin());
				else
					mRecv->NotifyStatus(NET_INFO, this, &std::string("接收到包检验码验证失败"));

				if (lstSize > 100)
					mRecv->NotifyStatus(NET_INFO, this, &(std::string("缓存") + std::to_string(lstSize) + std::string("条数据")));

				m_mutex.lock();
				m_pack_buffer.pop_front();
				m_mutex.unlock();
			}
			catch (std::exception&)
			{
				m_mutex.unlock();
			}
		}
	});
}
void Network::ResetByHeader(char* data, size_t len)
{
	size_t nlen = len;
	while (len > 0)
	{
		PACKAGE_INFO* package = (PACKAGE_INFO*)(data + nlen - len);
		if (len >= FIELD_OFFSET(PACKAGE_INFO, data) + package->datalen && strcmp(netpack_magic, package->magic) == 0){

			std::vector<char> temp_buffer(FIELD_OFFSET(PACKAGE_INFO, data) + package->datalen, 0);
			size_t nCopyData = (size_t)package->datalen + FIELD_OFFSET(PACKAGE_INFO, data);
			m_mutex.lock();
			memcpy(&(*temp_buffer.begin()), data + nlen - len, nCopyData);
			m_pack_buffer.push_back(temp_buffer);
			m_mutex.unlock();
			len -= nCopyData;
		}
		else{
			size_t data_len = (strcmp(netpack_magic, package->magic) == 0?
				sizeof(PACKAGE_INFO) : (m_temp_buffer.size()>= sizeof(PACKAGE_INFO)?
				FIELD_OFFSET(PACKAGE_INFO, data) + ((PACKAGE_INFO*)&m_temp_buffer[0])->datalen : sizeof(PACKAGE_INFO)));
			m_temp_buffer.insert(m_temp_buffer.end(), len, 0);
			memcpy(&(*m_temp_buffer.begin()) + m_temp_buffer.size() - len, data + nlen - len, len);
			len = 0;

			PACKAGE_INFO* pack = (PACKAGE_INFO*)(&(*m_temp_buffer.begin()));
			while (m_temp_buffer.size() >= data_len){
				if (strcmp(netpack_magic, pack->magic) == 0 && m_temp_buffer.size() >= pack->datalen + FIELD_OFFSET(PACKAGE_INFO, data))
				{
					m_mutex.lock();
					m_pack_buffer.push_back(m_temp_buffer);
					m_temp_buffer.erase(m_temp_buffer.begin(), m_temp_buffer.begin() +
						pack->datalen + FIELD_OFFSET(PACKAGE_INFO, data));
					m_mutex.unlock();
				}
				else if(strcmp(netpack_magic, pack->magic) == 0)
				{
					break;
				}
				else
				{
					m_temp_buffer.erase(m_temp_buffer.begin());
				}
			}
		}
	}
}